package com.ben.multiple.util;

import java.util.List;

/**
 * @author BenJin Yin
 * @date 2020/4/8
 */
public interface GpsUtils {

    /**
     * 判断点与多边形的位置关系
     *
     * @param x         坐标1
     * @param y         坐标2
     * @param pointList 多边形坐标集合，坐标1、坐标2
     * @return Relation
     */
    static Relation getPositionRelation(double x, double y, List<double[]> pointList) {
        if (pointList == null || pointList.size() < 3 || pointList.stream().anyMatch(obj -> obj.length != 2)) {
            return null;
        }
        // 1.点在边界上
        // 1.1 点重合
        if (pointList.stream().anyMatch(obj -> obj[0] == x && obj[1] == y)) {
            return Relation.ON;
        }
        // 1.2 点在线上
        for (int i = 0; i < pointList.size(); i++) {
            double[] point1 = pointList.get(i);
            double[] point2;
            if (i == pointList.size() - 1) {
                point2 = pointList.get(0);
            } else {
                point2 = pointList.get(i + 1);
            }
            // 垂直的线
            if (x == point1[0] && x == point2[0] && y >= Math.min(point1[1], point2[1]) && y <= Math.max(point1[1], point2[1])) {
                return Relation.ON;
            }
            // 水平的线
            if (y == point1[1] && y == point2[1] && x >= Math.min(point1[0], point2[0]) && x <= Math.max(point1[0], point2[0])) {
                return Relation.ON;
            }
            // 斜线，斜率相等
            if ((x - point1[0]) * (point2[1] - point1[1]) / (point2[0] - point1[0]) == (y - point1[1])) {
                if (y > Math.min(point1[1], point2[1]) && y < Math.max(point1[1], point2[1])) {
                    return Relation.ON;
                }
            }
        }
        // 2 射线法；规则：点向右水平做一条射线，与边界的交点是偶数个，则点在外，与边界的交点是奇数个，则点在内；
        // 特殊情况：射线与顶点相交 y = y1 || y = y2， y = max (y1,y2) ==> 不算交点 ； y = min (y1,y2) ==> 算一个交点；
        // 射线与线重合，不算交点
        int interSectionCount = 0;
        for (int i = 0; i < pointList.size(); i++) {
            double[] point1 = pointList.get(i);
            double[] point2;
            if (i == pointList.size() - 1) {
                point2 = pointList.get(0);
            } else {
                point2 = pointList.get(i + 1);
            }
            // 存在交点的初步条件：x <= max(x1,x2) && y>=min(y1,y2) && y<=max(y1,y2)
            if (x > Math.max(point1[0], point2[0]) || y > Math.max(point1[1], point2[1]) || y < Math.min(point1[1], point2[1])) {
                continue;
            }
            // 射线与线段重合时不计数； 线段水平，但是不重合，不可能有交点
            if (point1[1] == point2[1]) {
                continue;
            }
            // 存在交点的条件：点在 线段的左侧；公式： （y - y1）*(x2 - x1) / (y2 - y1)  >  (x - x1)  (相等时，点在线上)
            if ((y - point1[1]) * (point2[0] - point1[0]) / (point2[1] - point1[1]) < (x - point1[0])) {
                continue;
            }
            // 交点是顶点；y = max (y1,y2) ==> 不算交点
            if (y == Math.max(point1[1], point2[1])) {
                continue;
            }
            interSectionCount++;
        }
        return interSectionCount % 2 == 0 ? Relation.OUT : Relation.IN;
    }

    enum Relation {
        /**
         * 点和多边形的位置关系
         */
        IN("1", "在内"),
        ON("2", "在边界上"),
        OUT("3", "在外");

        private String code;
        private String remark;

        Relation(String code, String remark) {
            this.code = code;
            this.remark = remark;
        }

        public String getCode() {
            return code;
        }

        public String getRemark() {
            return remark;
        }
    }
}
